/**
 * Copyright (c) 2016-2020 https://github.com/zhaohuatai
 *
 * contact 824069438@qq.com
 *  
 */
package org.zfes.snowy.auth.shiro.credential;


import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.zfes.snowy.auth.shiro.jwt.token.JwtAuthToken;
import org.zfes.snowy.core.util.ZStrUtil;
/**
 * 如果来自于jwtAuthFilter---username-password 验证直接通过，不用再进行密码匹配验证<br>
 * 如果不配置登陆次数---直接调用父类 验证函数<br>
 * 
 * @author zhaohuatai
 *
 */
public class SnowyLimitHashedCredentialsMatcher extends HashedCredentialsMatcher{
	
		private String retryLimitCacheName;
		
		private Integer maxRetryCount;
		
		private Integer lockingTime;
		
		private Boolean isEnableRetryLimit;
		
	    private Cache<String, RetryLock> passwordRetryLimitCache;//K,V===<username:retryCountt>

	    public SnowyLimitHashedCredentialsMatcher(CacheManager cacheManager) {
	    	
	    	String cacheName=!ZStrUtil.hasText(retryLimitCacheName)?"retryLimitCache":retryLimitCacheName;
	    	
	    	passwordRetryLimitCache = cacheManager.getCache(cacheName);
	    }
	    
	    @Override
	    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
	    	//如果来自于jwtAuthFilter---username-password 验证直接通过，不用再进行密码匹配验证
	    	if(token instanceof JwtAuthToken){
	    		return true;
	    	}
	    	//如果不配置登陆次数---直接调用父类 验证函数
	    	if(isEnableRetryLimit==null||!isEnableRetryLimit){
	    		return super.doCredentialsMatch(token, info);
	    	}
	        String username = (String)token.getPrincipal();
	        
	        RetryLock retryLock= passwordRetryLimitCache.get(username);
	        long nowTime=new Date().getTime();
	        if(retryLock == null) {
	        	retryLock=new RetryLock(new AtomicInteger(0), nowTime);
	            passwordRetryLimitCache.put(username, retryLock);
	        }
	        
	        AtomicInteger retryCount = retryLock.getRetryCount();
	        long lastRetryTime=retryLock.getLastRetryTime();
	        
	        if((nowTime-lastRetryTime)/1000.0<=lockingTime){
	        	int nowCount=retryCount.incrementAndGet();
	        	if( nowCount> maxRetryCount) {
	        		passwordRetryLimitCache.put(username, new RetryLock(new AtomicInteger(nowCount), nowTime));
	   	            throw new ExcessiveAttemptsException("username: " + username + " tried to login more than "+maxRetryCount+" times in period");
	   	        }
	        }else{
	        	passwordRetryLimitCache.remove(username);
	        }
	     

	        //调用super
	        boolean matches = super.doCredentialsMatch(token, info);
	        if(matches) {
	        	passwordRetryLimitCache.remove(username);
	        }
	        return matches;
	    }

		public String getRetryLimitCacheName() {
			return retryLimitCacheName;
		}

		public void setRetryLimitCacheName(String retryLimitCacheName) {
			this.retryLimitCacheName = retryLimitCacheName;
		}

		public Integer getMaxRetryCount() {
			return maxRetryCount;
		}

		public void setMaxRetryCount(Integer maxRetryCount) {
			this.maxRetryCount = maxRetryCount;
		}

		public Integer getLockingTime() {
			return lockingTime;
		}

		public void setLockingTime(Integer lockingTime) {
			this.lockingTime = lockingTime;
		}
}
